package com.simafei.flow.web.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.simafei.flow.web.common.Weightable;
import com.simafei.flow.web.dao.GroupMapper;
import com.simafei.flow.web.domain.resp.CategoryResp;
import com.simafei.flow.web.domain.resp.FlowResp;
import com.simafei.flow.web.domain.resp.FlowResultResp;
import com.simafei.flow.web.domain.resp.GroupResp;
import com.simafei.flow.web.entity.CategoryPO;
import com.simafei.flow.web.entity.FlowPO;
import com.simafei.flow.web.entity.GroupPO;
import com.simafei.flow.web.service.ICategoryService;
import com.simafei.flow.web.service.IFlowService;
import com.simafei.flow.web.service.IGroupService;
import com.simafei.flow.web.transfer.FlowTransfer;
import com.simafei.flow.web.transfer.GroupTransfer;
import com.simafei.flow.web.util.RouteUtils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author fengpengju
 * @since 2024-06-18
 */
@Service
@RequiredArgsConstructor
public class GroupServiceImpl extends ServiceImpl<GroupMapper, GroupPO> implements IGroupService {

    private final ICategoryService categoryService;

    private final IFlowService flowService;

    @Override
    public List<CategoryResp> listWithCategory() {
        List<CategoryResp> categoryFlows = new ArrayList<>();
        List<CategoryPO> categoryList = categoryService.list();
        if (categoryList.isEmpty()) {
            return categoryFlows;
        }

        Map<Long, List<GroupResp>> categoryGroupMap = getByCategoryIds(categoryList.stream().map(CategoryPO::getId)
                .collect(Collectors.toList()))
                .stream()
                .collect(Collectors.groupingBy(GroupResp::getCategoryId));
        for (CategoryPO po : categoryList) {
            CategoryResp categoryFlow = new CategoryResp();
            categoryFlow.setId(po.getId());
            categoryFlow.setIcon(po.getIcon());
            categoryFlow.setName(po.getName());
            categoryFlow.setGroups(categoryGroupMap.getOrDefault(po.getId(), new ArrayList<>()));
            categoryFlows.add(categoryFlow);
        }
        return categoryFlows;
    }

    @Override
    public FlowResultResp execute(Long groupId, Map<String, Object> param) {
        FlowWeightable route = getWeightable(groupId);

        return flowService.execute(route.getId(), param);
    }

    @Override
    public String executeAsync(Long groupId, Map<String, Object> param) {
        FlowWeightable route = getWeightable(groupId);
        return flowService.executeAsync(route.getId(), param);
    }

    @Override
    public boolean rename(Long groupId, String groupName, String groupDesc) {
        return update(new LambdaUpdateWrapper<>(GroupPO.class)
                .eq(GroupPO::getId, groupId)
                .set(GroupPO::getGroupName, groupName)
                .set(GroupPO::getGroupDesc, groupDesc));
    }

    private List<GroupResp> getByCategoryIds(List<Long> categoryIds) {
        List<GroupPO> records = list(new LambdaQueryWrapper<GroupPO>()
                .in(GroupPO::getCategoryId, categoryIds));

        List<GroupResp> voList = GroupTransfer.INSTANCE.toResp(records);

        List<Long> groupIds = voList.stream().map(GroupResp::getId).collect(Collectors.toList());
        if (groupIds.isEmpty()) {
            return voList;
        }

        Map<Long, List<FlowPO>> groupMap = flowService.list(new LambdaQueryWrapper<FlowPO>()
                        .select(FlowPO::getId, FlowPO::getFlowName,
                                FlowPO::getCategoryId,
                                FlowPO::getGroupId,
                                FlowPO::getVersionNo,
                                FlowPO::getWeight,
                                FlowPO::getStatus, FlowPO::getValid,
                                FlowPO::getCreateTime, FlowPO::getUpdateTime)
                        .in(FlowPO::getGroupId, groupIds))
                .stream()
                .collect(Collectors.groupingBy(FlowPO::getGroupId));

        for (GroupResp groupResp : voList) {
            List<FlowPO> flows = groupMap.getOrDefault(groupResp.getId(), new ArrayList<>());
            flows.sort((o1, o2) -> o2.getUpdateTime().compareTo(o1.getUpdateTime()));
            List<FlowResp> dtoList = FlowTransfer.INSTANCE.toResp(flows);
            groupResp.setFlows(dtoList);

            groupResp.setEmpty(dtoList.isEmpty());
        }
        return voList.stream().filter(vo -> Boolean.FALSE.equals(vo.getEmpty())).collect(Collectors.toList());
    }

    @Setter
    public static class FlowWeightable implements Weightable {
        private Integer weight;

        @Getter
        private Long id;

        @Override
        public int getWeight() {
            return weight;
        }
    }

    private FlowWeightable getWeightable(Long groupId) {
        List<FlowResp> decisionFlows = flowService.listByGroup(groupId);
        List<FlowWeightable> flows = decisionFlows.stream().map(flow -> {
            FlowWeightable weightable = new FlowWeightable();
            weightable.setId(flow.getId());
            weightable.setWeight(flow.getWeight());
            return weightable;
        }).collect(Collectors.toList());
        return RouteUtils.route(flows);
    }

}
